/**
 * @author gxl
 * @email 1515533507@qq.com
 * @time 2021/7/27
 * 
 * 拿到项目千万不要急，一步一步来，分析每一步，就关心当前的事情，不要看整体效果。
 * 
 * 首先，考虑灵活性，所有的一切都要是可计算的，需要有一个配置对象，到时候只要配置对象一改，整体就会改变
 * 
 * 
 * 
 */

//一：x项目的配置
let gameConfig = {
  totalWidth: 500, //总的宽度也就是图片的宽度
  totalHeight: 641, //总的高度，也就是图片的高度
  rows: 2, //行数
  cols: 2, //列数
  isVisible: false, //设置当前元素是否隐藏
  imgUrl: './5.jpg', //图片的路径
  blocksArray: [], //放置每一个块的数组
  dom: document.querySelector('#game') //获取到相应的dom对象
}
gameConfig.blockWidth = gameConfig.totalWidth / gameConfig.cols; //计算每一块的宽度
gameConfig.blockHeight = gameConfig.totalHeight / gameConfig.rows; //计算每一块的高度
gameConfig.blockNums = gameConfig.rows * gameConfig.cols; //总共有多少块




//利用解构获得相关的属性
let {
  dom,
  totalWidth,
  totalHeight,
  blockWidth,
  blockHeight,
  rows,
  cols,
  blocksArray,
  imgUrl,
  isVisible
} = gameConfig;


//初始化
init();

function init() {
  //初始化外层div
  initGameDom();
  //初始化内层div
  initSmallBlock();
  //打乱排序
  shuffle();
  //玩游戏
  play();

  //初始化外层大div
  function initGameDom() {
    setStyle(dom, {
      width: totalWidth,
      height: totalHeight,
      position: 'relative',
      border: '2px solid #ccc'
    })
  }

  //初始化每个小的div(我们希望，每个小div都是一个对象，里面包含着关于这个div的所有属性，包括正确的left,top值，和当前的left,top值)
  function initSmallBlock() {
    for (let i = 0; i < rows; i++) {
      for (let j = 0; j < cols; j++) {
        //最后一个元素需要设置隐藏
        if ((i == rows - 1) && (j == cols - 1)) {
          isVisible = true;
        }
        blocksArray.push(new Block(j * blockWidth, i * blockHeight, isVisible));
      }
    }
  }

  //数组洗牌
  function shuffle() {
    // blocksArray.sort(() => Math.random() - 0.5);
    for (let i = 0; i < blocksArray.length - 1; i++) {
      //产生一个随机数，将当前项与随机数的那一项交换left，top值
      let index = getRandom(0, blocksArray.length - 2);
      exchangeNode(blocksArray[i], blocksArray[index]);
    }
    //保证与原图一定是不一样的，不然就重新洗过
    if (isInOriginal()) {
      shuffle();
    }
    //重新显示
    blocksArray.forEach(item => item.show());
  }

  //
  function play() {
    //先找 display 为 none的元素
    let emptyNode = blocksArray.find(item => item.isVisible);
    //遍历数组，为数组中的每一项的dom添加点击事件
    blocksArray.forEach(item => {
      item.dom.onclick = function () {
        //判断一下，如果当前的元素和空的元素在同一行或者同一列，并且相减小于一个宽度，才可以交换
        if (emptyNode.top === item.top && Math.abs(emptyNode.left - item.left) === blockWidth ||
          emptyNode.left === item.left && Math.abs(emptyNode.top - item.top) === blockHeight) {
          console.log('可以移动')
          exchangeNode(emptyNode, item);
          if (isInOriginal()) {
            setTimeout(() => {
              window.alert('恭喜你赢了');
            }, 0);
          }
        }
      }
    })
  }



  //辅助函数
  /**
   * 产生一个min-max的随机数
   * @param {*} min 
   * @param {*} max 
   * @returns 
   */
  function getRandom(min, max) {
    return Math.floor(Math.random() * (max + 1 - min) + min);
  }
  /**
   * 
   * @param {*} target 需要设置样式的目标
   * @param {*} options 需要为目标设置选项的
   */
  function setStyle(target, options = {}) {
    for (const key in options) {
      if (typeof options[key] == 'number') {
        options[key] += 'px';
      }
      target.style[key] = options[key];
    }
  }
  /**
   * 交换连个元素的位置
   * @param {*} node1 
   * @param {*} node2 
   */
  function exchangeNode(node1, node2) {
    [node1.left, node2.left] = [node2.left, node1.left];
    [node1.top, node2.top] = [node2.top, node1.top];
    node1.show();
    node2.show();
  }
  /**
   * 根据不同的left,top值产生不同位置的div
   * @param {*} left div距离左边的位置
   * @param {*} top  div距离右边的位置
   */
  function Block(left, top, isVisible) {
    // 每个小方块就是一个对象，对象里面保存着小方块的left,top，correctLeft，correctTop，dom，isVisible(是否可见)，以及show方法
    this.left = left;
    this.top = top;
    this.correctLeft = this.left;
    this.correctTop = this.top;
    this.dom = document.createElement('div');
    this.isVisible = isVisible;
    setStyle(this.dom, {
      width: blockWidth,
      height: blockHeight,
      border: '1px solid #fff',
      background: `url("${imgUrl}") -${this.correctLeft}px -${this.correctTop}px`,
      position: 'absolute',
      cursor: 'pointer',
      boxSizing: 'border-box',
      display: isVisible ? 'none' : ''
    });
    dom.appendChild(this.dom);
    this.show = function () {
      setStyle(this.dom, {
        left: this.left,
        top: this.top
      })
    }
  }
  /**
   * 判断每个方块是否在原先的位置上
   * @returns 
   */
  function isInOriginal() {
    //循环数组，判断当前位置和正确的位置是否相等
    return blocksArray.every(item => item.left == item.correctLeft && item.top == item.correctTop)
  }
}